package com.tlgen.tomcat.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.tlgen.tomcat.model.FilterClasses;
import com.tlgen.tomcat.model.Request;
import com.tlgen.tomcat.model.RequestMethodAndUrl;
import com.tlgen.tomcat.model.Response;
import org.apache.commons.lang.StringEscapeUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.tlgen.tomcat.utils.AnnotationUtils.getFilterClassesWithAnnotation;

public class ReflectionUtils {

    public static Object invokeHttpRequest(RequestMethodAndUrl requestMethodAndUrl) {
        Object result;
        String className = requestMethodAndUrl.getClassName();
        try {
            Class<?> aClass = Class.forName(className);
            try {
                Object instance = aClass.newInstance();
                Class<?>[] parameterTypes = null;
                for (Method declaredMethod : aClass.getDeclaredMethods()) {
                    if (Objects.equals(requestMethodAndUrl.getMethodName(), declaredMethod.getName())) {
                        parameterTypes = declaredMethod.getParameterTypes();
                        break;
                    }
                }
                if (Objects.nonNull(parameterTypes) && parameterTypes.length > 0) {
                    if (Objects.nonNull(requestMethodAndUrl.getArgs())) {
                        // 判断方法参数的类型, 并设置正确的值类型
                        correctArgsToType(requestMethodAndUrl, parameterTypes);
                    } else {
                        throw new RuntimeException("参数不能为空");
                    }
                }
                // 精妙: Class<?>[] parameterTypes 可以支持 Class<?>... parameterTypes, 解决反射调用任意一个方法, 不确定方法参数的入参类型和个数
                Method method = aClass.getMethod(requestMethodAndUrl.getMethodName(), parameterTypes);
                // 为了支持手写的 speed-spring 框架, 在调用实际方法之前, 先获取 bean 对象
                Object bean = SpeedSpringContextUtils.getBean(aClass);
                if (Objects.nonNull(bean)) { // 如果 bean 对象不为空, 可以使用 bean 进行调用
                    result = method.invoke(bean, requestMethodAndUrl.getArgs());
                } else { // 如果没有 speed-spring, 也可以正常调用
                    result = method.invoke(instance, requestMethodAndUrl.getArgs());
                }
            } catch (InvocationTargetException | InstantiationException | NoSuchMethodException |
                     IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    /**
     * // 判断方法参数的类型, 并设置正确的值类型
     *
     * @param requestMethodAndUrl
     * @param parameterTypes
     */
    private static void correctArgsToType(RequestMethodAndUrl requestMethodAndUrl, Class<?>[] parameterTypes) {
        if (Objects.equals("GET", requestMethodAndUrl.getRequestType())) {
            doFormatType(requestMethodAndUrl, parameterTypes);
        } else if (Lists.newArrayList("POST", "PUT").contains(requestMethodAndUrl.getRequestType())) {
            convertRequestBodyToInstance(requestMethodAndUrl, parameterTypes);
        } else if (Objects.equals("DELETE", requestMethodAndUrl.getRequestType())) {
            convertRequestBodyToInstance(requestMethodAndUrl, parameterTypes);
        }

    }

    /**
     * 格式化属性, 设置正确的数据值类型
     *
     * @param requestMethodAndUrl
     * @param parameterTypes
     */
    private static void doFormatType(RequestMethodAndUrl requestMethodAndUrl, Class<?>[] parameterTypes) {
        for (int i = 0; i < parameterTypes.length; i++) {
            if (Objects.equals(parameterTypes[i].getTypeName(), Byte.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Byte.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), byte.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Byte.parseByte(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), Short.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Short.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), short.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Short.parseShort(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), Integer.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Integer.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), int.class.getTypeName())) {
                if (Objects.isNull(requestMethodAndUrl.getArgs()[i])) {
                    requestMethodAndUrl.getArgs()[i] = 0;
                } else {
                    requestMethodAndUrl.getArgs()[i] = Integer.parseInt(String.valueOf(requestMethodAndUrl.getArgs()[i]));
                }
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), Long.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Long.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), long.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Long.parseLong(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), Float.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Float.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), float.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Float.parseFloat(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), Double.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Double.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), double.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Double.parseDouble(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), Boolean.class.getTypeName())) {
                requestMethodAndUrl.getArgs()[i] = Boolean.valueOf(String.valueOf(requestMethodAndUrl.getArgs()[i]));
            }
            if (Objects.equals(parameterTypes[i].getTypeName(), boolean.class.getTypeName())) {
                if (Objects.isNull(requestMethodAndUrl.getArgs()[i])) {
                    requestMethodAndUrl.getArgs()[i] = 0;
                } else {
                    requestMethodAndUrl.getArgs()[i] = Boolean.parseBoolean(String.valueOf(requestMethodAndUrl.getArgs()[i]));
                }
            }
        }
    }

    /**
     * 将请求体转换成实际的引用数据参数类型
     *
     * @param requestMethodAndUrl
     * @param parameterTypes
     */
    private static void convertRequestBodyToInstance(RequestMethodAndUrl requestMethodAndUrl, Class<?>[] parameterTypes) {
        Object[] args = requestMethodAndUrl.getArgs();
        if (Objects.equals(0, requestMethodAndUrl.getBodyType())) { // 请求体类型 0.application-json
            String jsonString = JSONObject.toJSONString(args[0]);

            // 转义成 JSON 格式
            jsonString = jsonString.substring(1, jsonString.length() - 1);
            jsonString = jsonString.replace("\\n", " ");
            jsonString = StringEscapeUtils.unescapeJava(jsonString); // 去转义, 如把 \ 去掉

            JSONObject jsonObject = null;
            JSONArray jsonArray = null;
            // 判断是否是集合接口类型
            if (jsonString.startsWith("[")) {
                jsonArray = JSONArray.parseArray(jsonString);
            } else {
                jsonObject = JSONObject.parseObject(jsonString);
            }
            // 反射创建对象
            try {
                Class<?> aClass = Class.forName(parameterTypes[0].getName());
                if (aClass.getTypeName().contains("Collection")) { // 如果是 Collection 接口类型, 则无法直接进行实例化
                    List list = jsonArray;
                    // 设置接口类型参数
                    Object[] instanceArgs = new Object[1];
                    instanceArgs[0] = list;
                    requestMethodAndUrl.setArgs(instanceArgs);
                } else if (aClass.getTypeName().contains("List")) { // 如果是 List 接口类型, 则无法直接进行实例化
                    List list = jsonArray;
                    // 设置接口类型参数
                    Object[] instanceArgs = new Object[1];
                    instanceArgs[0] = list;
                    requestMethodAndUrl.setArgs(instanceArgs);
                } else if (aClass.getTypeName().contains("Map")) { // 如果是 Map 接口类型, 则无法直接进行实例化
                    Map objectMap = jsonObject.toJavaObject(Map.class);
                    // 设置接口类型参数
                    Object[] instanceArgs = new Object[1];
                    instanceArgs[0] = objectMap;
                    requestMethodAndUrl.setArgs(instanceArgs);
                } else { // 如果是普通类对象类型, 可以直接进行实例化
                    Object instance = aClass.newInstance();
                    Field[] declaredFields = aClass.getDeclaredFields();
                    for (Field declaredField : declaredFields) {
                        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
                            if (Objects.equals(declaredField.getName(), entry.getKey())) {
                                // 根据字段名称获取指定的 Field 对象
                                Field field = aClass.getDeclaredField(declaredField.getName());
                                field.setAccessible(true);
                                // 设置字段的新值
                                field.set(instance, entry.getValue());
                            }
                        }
                    }
                    // 设置对象类型参数
                    Object[] instanceArgs = new Object[1];
                    instanceArgs[0] = instance;
                    requestMethodAndUrl.setArgs(instanceArgs);
                }
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException |
                     NoSuchFieldException e) {
                throw new RuntimeException(e);
            }
        } else if (Objects.equals(1, requestMethodAndUrl.getBodyType())) { // 请求体类型 1.form-data
            doFormatType(requestMethodAndUrl, parameterTypes);
        }
    }

    /**
     * 调用请求拦截方法
     *
     * @param className
     * @param request
     */
    public static void invokeRequestFilterMethod(String className, Request request) {
        try {
            Class<?> aClass = Class.forName(className);
            try {
                Object instance = aClass.newInstance();
                Method declaredMethod = aClass.getDeclaredMethod("setRequest", Request.class);
                declaredMethod.invoke(instance, request);
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                     NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 调用响应拦截方法
     *
     * @param className
     * @param response
     */
    public static void invokeResponseFilterMethod(String className, Request request, Response response) {
        try {
            Class<?> aClass = Class.forName(className);
            try {
                Object instance = aClass.newInstance();
                Method declaredMethod = aClass.getDeclaredMethod("setResponse", Request.class, Response.class);
                declaredMethod.invoke(instance, request, response);
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                     NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 请求拦截
     *
     * @param request
     */
    public static void doRequestFilter(Request request) {
        List<FilterClasses> filterClassesWithAnnotation = getFilterClassesWithAnnotation();
        for (FilterClasses filterClasses : filterClassesWithAnnotation) {
            invokeRequestFilterMethod(filterClasses.getClassName(), request);
        }
    }

    /**
     * 响应拦截
     *
     * @param request
     * @param response
     */
    public static void doResponseFilter(Request request, Response response) {
        List<FilterClasses> filterClassesWithAnnotation = getFilterClassesWithAnnotation();
        for (FilterClasses filterClasses : filterClassesWithAnnotation) {
            invokeResponseFilterMethod(filterClasses.getClassName(), request, response);
        }
    }

}
